// Bristleback plugin - Copyright (c) 2010 bristleback.googlecode.com
// ---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
// This library is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of merchantability
// or fitness for a particular purpose.
// You should have received a copy of the GNU Lesser General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
// ---------------------------------------------------------------------------
package pl.bristleback.server.bristle.integration.spring;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;

/**
 * Util class which can enable integration with Spring dependency injection.
 * To enable integration, one of existing application context setter methods must be invoked before jwebsocket framework starts.
 * In current version, enabling Spring integration is tested only with web applications.
 * <p/>
 * Created on: 2010-10-03 13:37:28 <br/>
 *
 * @author Wojciech Niemiec
 */
public final class SpringIntegrationUtil {
  private static Logger log = Logger.getLogger(SpringIntegrationUtil.class.getName());

  private SpringIntegrationUtil() {
    throw new UnsupportedOperationException();
  }

  /**
   * Gets previously loaded application context. This method throws an exception if application context is not set yet.
   *
   * @return application context.
   */
  public static ApplicationContext getApplicationContext() {
    return ContextHolder.INSTANCE.getApplicationContext();
  }

  /**
   * Sets application context from given servlet context.
   * Servlet context is available from several places, eg. in classes implementing ServletContextListener interface
   * from javax.servlet package.
   *
   * @param servletContext servlet context.
   */
  public static void setApplicationContextFromServlet(ServletContext servletContext) {
    ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    ContextHolder.INSTANCE.setApplicationContext(context);
  }

  /**
   * Directly sets application context. Method is not tested yet, although it should work properly.
   *
   * @param context application context
   */
  public static void setApplicationContext(ApplicationContext context) {
    ContextHolder.INSTANCE.setApplicationContext(context);
  }

  /**
   * Gets spring bean from previously given application context. Bean type is not checked.
   * This method can be used from anywhere in application however it is not recommended.
   *
   * @param ref Spring reference name
   * @return bean
   */
  public static Object getSpringBean(String ref) {
    return ContextHolder.INSTANCE.getApplicationContext().getBean(ref);
  }

  public static boolean containsBean(String ref) {
    return ContextHolder.INSTANCE.getApplicationContext().containsBean(ref);
  }

  /**
   * Gets spring bean from previously given application context.
   * Bean type check is performed and method throws an exception if the type is not matching.
   * This method can be used from anywhere in application however it is not recommended.
   *
   * @param ref      Spring reference name
   * @param beanType exact type of bean or parent.
   * @return bean with proper type
   */
  @SuppressWarnings("unchecked")
  public static <T> T getSpringBean(String ref, Class<T> beanType) {
    return (T) (ContextHolder.INSTANCE.getApplicationContext().getBean(ref, beanType));
  }

  private enum ContextHolder {

    INSTANCE;

    private ApplicationContext applicationContext;

    private void setApplicationContext(ApplicationContext ac) {
      if (applicationContext != null) {
        throw new IllegalStateException("Context already set");
      }
      applicationContext = ac;
      BristleSpringIntegration.enableIntegration();
    }

    private ApplicationContext getApplicationContext() {
      if (applicationContext == null) {
        throw new IllegalStateException("Context not set yet, "
          + "enable Spring Framework integration in configuration or "
          + "use " + SpringIntegrationUtil.class.getSimpleName() + ".");
      }
      return applicationContext;
    }

  }
}
